package org.spring.demo.wx;

import java.util.HashMap;
import java.util.Map;

import org.spring.demo.wx.WXPayConstants.SignType;




public class WXPay {
	 private WXPayConfig config;
	    private SignType signType;
	    private boolean autoReport;
	    private boolean useSandbox;
	    private String notifyUrl;
	    private WXPayRequest wxPayRequest;

	    public WXPay(final WXPayConfig config) throws Exception {
	        this(config, null, true, false);
	    }

	    public WXPay(final WXPayConfig config, final boolean autoReport) throws Exception {
	        this(config, null, autoReport, false);
	    }


	    public WXPay(final WXPayConfig config, final boolean autoReport, final boolean useSandbox) throws Exception{
	        this(config, null, autoReport, useSandbox);
	    }

	    public WXPay(final WXPayConfig config, final String notifyUrl) throws Exception {
	        this(config, notifyUrl, true, false);
	    }

	    public WXPay(final WXPayConfig config, final String notifyUrl, final boolean autoReport) throws Exception {
	        this(config, notifyUrl, autoReport, false);
	    }

	    public WXPay(final WXPayConfig config, final String notifyUrl, final boolean autoReport, final boolean useSandbox) throws Exception {
	        this.config = config;
	        this.notifyUrl = notifyUrl;
	        this.autoReport = autoReport;
	        this.useSandbox = useSandbox;
	       /* if (useSandbox) {*/
	            this.signType = SignType.MD5; 
	        /*}
	        else {
	            this.signType = SignType.HMACSHA256;
	        }*/
	        this.wxPayRequest = new WXPayRequest(config);
	    }

	    private void checkWXPayConfig() throws Exception {
	        if (this.config == null) {
	            throw new Exception("config is null");
	        }
	        if (this.config.getAppId() == null || this.config.getAppId().trim().length() == 0) {
	            throw new Exception("appid in config is empty");
	        }
	        if (this.config.getAppId() == null || this.config.getAppId().trim().length() == 0) {
	            throw new Exception("appid in config is empty");
	        }
	        if (this.config.getCertStream() == null) {
	            throw new Exception("cert stream in config is empty");
	        }
//	        if (this.config.getPrimaryDomain() == null || this.config.getPrimaryDomain().trim().length() == 0) {
//	            throw new Exception("primary domain in config is empty");
//	        }
	//
//	        // todo 海外就填两个相同的？ 下面的逻辑待考虑
//	        if (this.config.getAlternateDomain() == null || this.config.getAlternateDomain().trim().length() == 0) {
//	            throw new Exception("alternate domain in config is empty");
//	        }
	        if (this.config.getIwxPayDomain() == null){
	            throw new Exception("config.getWXPayDomain() is null");
	        }

	        if (this.config.getHttpConnectTimeoutMs() < 10) {
	            throw new Exception("http connect timeout is too small");
	        }
	        if (this.config.getHttpReadTimeoutMs() < 10) {
	            throw new Exception("http read timeout is too small");
	        }

	    }

	    /**
	     * 向 Map 中添加 appid、mch_id、nonce_str、sign_type、sign <br>
	     * 该函数适用于商户适用于统一下单等接口，不适用于红包、代金券接口
	     *
	     * @param reqData
	     * @return
	     * @throws Exception
	     */
	    public Map<String, String> fillRequestData(Map<String, String> reqData) throws Exception {
	        reqData.put("appid", config.getAppId());
	        reqData.put("mch_id", config.getMchid());
	        reqData.put("nonce_str", WXPayUtil.generateUUID());
	        if (SignType.MD5.equals(this.signType)) {
	            reqData.put("sign_type", WXPayConstants.MD5);
	        }
	        else if (SignType.HMACSHA256.equals(this.signType)) {
	            reqData.put("sign_type", WXPayConstants.HMACSHA256);
	        }
	        reqData.put("sign", WXPayUtil.generateSignature(reqData, config.getKey(), this.signType));
	        return reqData;
	    }

	    /**
	     * 判断xml数据的sign是否有效，必须包含sign字段，否则返回false。
	     *
	     * @param reqData 向wxpay post的请求数据
	     * @return 签名是否有效
	     * @throws Exception
	     */
	    public boolean isResponseSignatureValid(Map<String, String> reqData) throws Exception {
	        // 返回数据的签名方式和请求中给定的签名方式是一致的
	        return WXPayUtil.isSignatureValid(reqData, this.config.getKey(), this.signType);
	    }

	    /**
	     * 判断支付结果通知中的sign是否有效
	     *
	     * @param reqData 向wxpay post的请求数据
	     * @return 签名是否有效
	     * @throws Exception
	     */
	    public boolean isPayResultNotifySignatureValid(Map<String, String> reqData) throws Exception {
	        String signTypeInData = reqData.get(WXPayConstants.FIELD_SIGN_TYPE);
	        SignType signType;
	        if (signTypeInData == null) {
	            signType = SignType.MD5;
	        }
	        else {
	            signTypeInData = signTypeInData.trim();
	            if (signTypeInData.length() == 0) {
	                signType = SignType.MD5;
	            }
	            else if (WXPayConstants.MD5.equals(signTypeInData)) {
	                signType = SignType.MD5;
	            }
	            else if (WXPayConstants.HMACSHA256.equals(signTypeInData)) {
	                signType = SignType.HMACSHA256;
	            }
	            else {
	                throw new Exception(String.format("Unsupported sign_type: %s", signTypeInData));
	            }
	        }
	        return WXPayUtil.isSignatureValid(reqData, this.config.getKey(), signType);
	    }


	    /**
	     * 不需要证书的请求
	     * @param urlSuffix String
	     * @param reqData 向wxpay post的请求数据
	     * @param connectTimeoutMs 超时时间，单位是毫秒
	     * @param readTimeoutMs 超时时间，单位是毫秒
	     * @return API返回数据
	     * @throws Exception
	     */
	    public String requestWithoutCert(String urlSuffix, Map<String, String> reqData,
	                                     int connectTimeoutMs, int readTimeoutMs) throws Exception {
	        String msgUUID = reqData.get("nonce_str");
	        String reqBody = WXPayUtil.mapToXml(reqData);

	        String resp = this.wxPayRequest.requestWithoutCert(urlSuffix, msgUUID, reqBody, connectTimeoutMs, readTimeoutMs, autoReport);
	        return resp;
	    }


	    /**
	     * 需要证书的请求
	     * @param urlSuffix String
	     * @param reqData 向wxpay post的请求数据  Map
	     * @param connectTimeoutMs 超时时间，单位是毫秒
	     * @param readTimeoutMs 超时时间，单位是毫秒
	     * @return API返回数据
	     * @throws Exception
	     */
	    public String requestWithCert(String urlSuffix, Map<String, String> reqData,
	                                  int connectTimeoutMs, int readTimeoutMs) throws Exception {
	        String msgUUID= reqData.get("nonce_str");
	        String reqBody = WXPayUtil.mapToXml(reqData);

	        String resp = this.wxPayRequest.requestWithCert(urlSuffix, msgUUID, reqBody, connectTimeoutMs, readTimeoutMs, this.autoReport);
	        return resp;
	    }

	    /**
	     * 处理 HTTPS API返回数据，转换成Map对象。return_code为SUCCESS时，验证签名。
	     * @param xmlStr API返回的XML格式数据
	     * @return Map类型数据
	     * @throws Exception
	     */
	    public Map<String, String> processResponseXml(String xmlStr) throws Exception {
	        String RETURN_CODE = "return_code";
	        String return_code;
	        Map<String, String> respData = WXPayUtil.xmlToMap(xmlStr);
	        if (respData.containsKey(RETURN_CODE)) {
	            return_code = respData.get(RETURN_CODE);
	        }
	        else {
	            throw new Exception(String.format("No `return_code` in XML: %s", xmlStr));
	        }

	        if (return_code.equals(WXPayConstants.FAIL)) {
	            return respData;
	        }
	        else if (return_code.equals(WXPayConstants.SUCCESS)) {
	           if (this.isResponseSignatureValid(respData)) {
	               return respData;
	           }
	           else {
	               throw new Exception(String.format("Invalid sign value in XML: %s", xmlStr));
	           }
	        }
	        else {
	            throw new Exception(String.format("return_code value %s is invalid in XML: %s", return_code, xmlStr));
	        }
	    }

	    /**
	     * 作用：提交刷卡支付<br>
	     * 场景：刷卡支付
	     * @param reqData 向wxpay post的请求数据
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> microPay(Map<String, String> reqData) throws Exception {
	        return this.microPay(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
	    }


	    /**
	     * 作用：提交刷卡支付<br>
	     * 场景：刷卡支付
	     * @param reqData 向wxpay post的请求数据
	     * @param connectTimeoutMs 连接超时时间，单位是毫秒
	     * @param readTimeoutMs 读超时时间，单位是毫秒
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> microPay(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
	        String url;
	        if (this.useSandbox) {
	            url = WXPayConstants.SANDBOX_MICROPAY_URL_SUFFIX;
	        }
	        else {
	            url = WXPayConstants.MICROPAY_URL_SUFFIX;
	        }
	        String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
	        return this.processResponseXml(respXml);
	    }

	    /**
	     * 提交刷卡支付，针对软POS，尽可能做成功
	     * 内置重试机制，最多60s
	     * @param reqData
	     * @return
	     * @throws Exception
	     */
	    public Map<String, String> microPayWithPos(Map<String, String> reqData) throws Exception {
	        return this.microPayWithPos(reqData, this.config.getHttpConnectTimeoutMs());
	    }

	    /**
	     * 提交刷卡支付，针对软POS，尽可能做成功
	     * 内置重试机制，最多60s
	     * @param reqData
	     * @param connectTimeoutMs
	     * @return
	     * @throws Exception
	     */
	    public Map<String, String> microPayWithPos(Map<String, String> reqData, int connectTimeoutMs) throws Exception {
	        int remainingTimeMs = 60*1000;
	        long startTimestampMs = 0;
	        Map<String, String> lastResult = null;
	        Exception lastException = null;

	        while (true) {
	            startTimestampMs = WXPayUtil.getCurrentTimestampMs();
	            int readTimeoutMs = remainingTimeMs - connectTimeoutMs;
	            if (readTimeoutMs > 1000) {
	                try {
	                    lastResult = this.microPay(reqData, connectTimeoutMs, readTimeoutMs);
	                    String returnCode = lastResult.get("return_code");
	                    if (returnCode.equals("SUCCESS")) {
	                        String resultCode = lastResult.get("result_code");
	                        String errCode = lastResult.get("err_code");
	                        if (resultCode.equals("SUCCESS")) {
	                            break;
	                        }
	                        else {
	                            // 看错误码，若支付结果未知，则重试提交刷卡支付
	                            if (errCode.equals("SYSTEMERROR") || errCode.equals("BANKERROR") || errCode.equals("USERPAYING")) {
	                                remainingTimeMs = remainingTimeMs - (int)(WXPayUtil.getCurrentTimestampMs() - startTimestampMs);
	                                if (remainingTimeMs <= 100) {
	                                    break;
	                                }
	                                else {
	                                    WXPayUtil.getLogger().info("microPayWithPos: try micropay again");
	                                    if (remainingTimeMs > 5*1000) {
	                                        Thread.sleep(5*1000);
	                                    }
	                                    else {
	                                        Thread.sleep(1*1000);
	                                    }
	                                    continue;
	                                }
	                            }
	                            else {
	                                break;
	                            }
	                        }
	                    }
	                    else {
	                        break;
	                    }
	                }
	                catch (Exception ex) {
	                    lastResult = null;
	                    lastException = ex;
	                }
	            }
	            else {
	                break;
	            }
	        }

	        if (lastResult == null) {
	            throw lastException;
	        }
	        else {
	            return lastResult;
	        }
	    }



	    /**
	     * 作用：统一下单<br>
	     * 场景：公共号支付、扫码支付、APP支付
	     * @param reqData 向wxpay post的请求数据
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> unifiedOrder(Map<String, String> reqData) throws Exception {
	        return this.unifiedOrder(reqData, config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
	    }


	    /**
	     * 作用：统一下单<br>
	     * 场景：公共号支付、扫码支付、APP支付
	     * @param reqData 向wxpay post的请求数据
	     * @param connectTimeoutMs 连接超时时间，单位是毫秒
	     * @param readTimeoutMs 读超时时间，单位是毫秒
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> unifiedOrder(Map<String, String> reqData,  int connectTimeoutMs, int readTimeoutMs) throws Exception {
	        String url;
	        if (this.useSandbox) {
	            url = WXPayConstants.SANDBOX_UNIFIEDORDER_URL_SUFFIX;
	        }
	        else {
	            url = WXPayConstants.UNIFIEDORDER_URL_SUFFIX;
	        }
	        if(this.notifyUrl != null) {
	            reqData.put("notify_url", this.notifyUrl);
	        }
	        String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
	        System.out.println("------------------------------------------------------------");
	        System.out.println(respXml);
	        System.out.println("------------------------------------------------------------");
	        return this.processResponseXml(respXml);
	    }


	    /**
	     * 作用：查询订单<br>
	     * 场景：刷卡支付、公共号支付、扫码支付、APP支付
	     * @param reqData 向wxpay post的请求数据
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> orderQuery(Map<String, String> reqData) throws Exception {
	        return this.orderQuery(reqData, config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
	    }


	    /**
	     * 作用：查询订单<br>
	     * 场景：刷卡支付、公共号支付、扫码支付、APP支付
	     * @param reqData 向wxpay post的请求数据 int
	     * @param connectTimeoutMs 连接超时时间，单位是毫秒
	     * @param readTimeoutMs 读超时时间，单位是毫秒
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> orderQuery(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
	        String url;
	        if (this.useSandbox) {
	            url = WXPayConstants.SANDBOX_ORDERQUERY_URL_SUFFIX;
	        }
	        else {
	            url = WXPayConstants.ORDERQUERY_URL_SUFFIX;
	        }
	        String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
	        return this.processResponseXml(respXml);
	    }


	    /**
	     * 作用：撤销订单<br>
	     * 场景：刷卡支付
	     * @param reqData 向wxpay post的请求数据
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> reverse(Map<String, String> reqData) throws Exception {
	        return this.reverse(reqData, config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
	    }


	    /**
	     * 作用：撤销订单<br>
	     * 场景：刷卡支付<br>
	     * 其他：需要证书
	     * @param reqData 向wxpay post的请求数据
	     * @param connectTimeoutMs 连接超时时间，单位是毫秒
	     * @param readTimeoutMs 读超时时间，单位是毫秒
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> reverse(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
	        String url;
	        if (this.useSandbox) {
	            url = WXPayConstants.SANDBOX_REVERSE_URL_SUFFIX;
	        }
	        else {
	            url = WXPayConstants.REVERSE_URL_SUFFIX;
	        }
	        String respXml = this.requestWithCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
	        return this.processResponseXml(respXml);
	    }


	    /**
	     * 作用：关闭订单<br>
	     * 场景：公共号支付、扫码支付、APP支付
	     * @param reqData 向wxpay post的请求数据
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> closeOrder(Map<String, String> reqData) throws Exception {
	        return this.closeOrder(reqData, config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
	    }


	    /**
	     * 作用：关闭订单<br>
	     * 场景：公共号支付、扫码支付、APP支付
	     * @param reqData 向wxpay post的请求数据
	     * @param connectTimeoutMs 连接超时时间，单位是毫秒
	     * @param readTimeoutMs 读超时时间，单位是毫秒
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> closeOrder(Map<String, String> reqData,  int connectTimeoutMs, int readTimeoutMs) throws Exception {
	        String url;
	        if (this.useSandbox) {
	            url = WXPayConstants.SANDBOX_CLOSEORDER_URL_SUFFIX;
	        }
	        else {
	            url = WXPayConstants.CLOSEORDER_URL_SUFFIX;
	        }
	        String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
	        return this.processResponseXml(respXml);
	    }


	    /**
	     * 作用：申请退款<br>
	     * 场景：刷卡支付、公共号支付、扫码支付、APP支付
	     * @param reqData 向wxpay post的请求数据
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> refund(Map<String, String> reqData) throws Exception {
	        return this.refund(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
	    }


	    /**
	     * 作用：申请退款<br>
	     * 场景：刷卡支付、公共号支付、扫码支付、APP支付<br>
	     * 其他：需要证书
	     * @param reqData 向wxpay post的请求数据
	     * @param connectTimeoutMs 连接超时时间，单位是毫秒
	     * @param readTimeoutMs 读超时时间，单位是毫秒
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> refund(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
	        String url;
	        if (this.useSandbox) {
	            url = WXPayConstants.SANDBOX_REFUND_URL_SUFFIX;
	        }
	        else {
	            url = WXPayConstants.REFUND_URL_SUFFIX;
	        }
	        String respXml = this.requestWithCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
	        return this.processResponseXml(respXml);
	    }


	    /**
	     * 作用：退款查询<br>
	     * 场景：刷卡支付、公共号支付、扫码支付、APP支付
	     * @param reqData 向wxpay post的请求数据
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> refundQuery(Map<String, String> reqData) throws Exception {
	        return this.refundQuery(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
	    }


	    /**
	     * 作用：退款查询<br>
	     * 场景：刷卡支付、公共号支付、扫码支付、APP支付
	     * @param reqData 向wxpay post的请求数据
	     * @param connectTimeoutMs 连接超时时间，单位是毫秒
	     * @param readTimeoutMs 读超时时间，单位是毫秒
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> refundQuery(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
	        String url;
	        if (this.useSandbox) {
	            url = WXPayConstants.SANDBOX_REFUNDQUERY_URL_SUFFIX;
	        }
	        else {
	            url = WXPayConstants.REFUNDQUERY_URL_SUFFIX;
	        }
	        String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
	        return this.processResponseXml(respXml);
	    }


	    /**
	     * 作用：对账单下载（成功时返回对账单数据，失败时返回XML格式数据）<br>
	     * 场景：刷卡支付、公共号支付、扫码支付、APP支付
	     * @param reqData 向wxpay post的请求数据
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> downloadBill(Map<String, String> reqData) throws Exception {
	        return this.downloadBill(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
	    }


	    /**
	     * 作用：对账单下载<br>
	     * 场景：刷卡支付、公共号支付、扫码支付、APP支付<br>
	     * 其他：无论是否成功都返回Map。若成功，返回的Map中含有return_code、return_msg、data，
	     *      其中return_code为`SUCCESS`，data为对账单数据。
	     * @param reqData 向wxpay post的请求数据
	     * @param connectTimeoutMs 连接超时时间，单位是毫秒
	     * @param readTimeoutMs 读超时时间，单位是毫秒
	     * @return 经过封装的API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> downloadBill(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
	        String url;
	        if (this.useSandbox) {
	            url = WXPayConstants.SANDBOX_DOWNLOADBILL_URL_SUFFIX;
	        }
	        else {
	            url = WXPayConstants.DOWNLOADBILL_URL_SUFFIX;
	        }
	        String respStr = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs).trim();
	        Map<String, String> ret;
	        // 出现错误，返回XML数据
	        if (respStr.indexOf("<") == 0) {
	            ret = WXPayUtil.xmlToMap(respStr);
	        }
	        else {
	            // 正常返回csv数据
	            ret = new HashMap<String, String>();
	            ret.put("return_code", WXPayConstants.SUCCESS);
	            ret.put("return_msg", "ok");
	            ret.put("data", respStr);
	        }
	        return ret;
	    }


	    /**
	     * 作用：交易保障<br>
	     * 场景：刷卡支付、公共号支付、扫码支付、APP支付
	     * @param reqData 向wxpay post的请求数据
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> report(Map<String, String> reqData) throws Exception {
	        return this.report(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
	    }


	    /**
	     * 作用：交易保障<br>
	     * 场景：刷卡支付、公共号支付、扫码支付、APP支付
	     * @param reqData 向wxpay post的请求数据
	     * @param connectTimeoutMs 连接超时时间，单位是毫秒
	     * @param readTimeoutMs 读超时时间，单位是毫秒
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> report(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
	        String url;
	        if (this.useSandbox) {
	            url = WXPayConstants.SANDBOX_REPORT_URL_SUFFIX;
	        }
	        else {
	            url = WXPayConstants.REPORT_URL_SUFFIX;
	        }
	        String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
	        return WXPayUtil.xmlToMap(respXml);
	    }


	    /**
	     * 作用：转换短链接<br>
	     * 场景：刷卡支付、扫码支付
	     * @param reqData 向wxpay post的请求数据
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> shortUrl(Map<String, String> reqData) throws Exception {
	        return this.shortUrl(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
	    }


	    /**
	     * 作用：转换短链接<br>
	     * 场景：刷卡支付、扫码支付
	     * @param reqData 向wxpay post的请求数据
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> shortUrl(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
	        String url;
	        if (this.useSandbox) {
	            url = WXPayConstants.SANDBOX_SHORTURL_URL_SUFFIX;
	        }
	        else {
	            url = WXPayConstants.SHORTURL_URL_SUFFIX;
	        }
	        String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
	        return this.processResponseXml(respXml);
	    }


	    /**
	     * 作用：授权码查询OPENID接口<br>
	     * 场景：刷卡支付
	     * @param reqData 向wxpay post的请求数据
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> authCodeToOpenid(Map<String, String> reqData) throws Exception {
	        return this.authCodeToOpenid(reqData, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
	    }


	    /**
	     * 作用：授权码查询OPENID接口<br>
	     * 场景：刷卡支付
	     * @param reqData 向wxpay post的请求数据
	     * @param connectTimeoutMs 连接超时时间，单位是毫秒
	     * @param readTimeoutMs 读超时时间，单位是毫秒
	     * @return API返回数据
	     * @throws Exception
	     */
	    public Map<String, String> authCodeToOpenid(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception {
	        String url;
	        if (this.useSandbox) {
	            url = WXPayConstants.SANDBOX_AUTHCODETOOPENID_URL_SUFFIX;
	        }
	        else {
	            url = WXPayConstants.AUTHCODETOOPENID_URL_SUFFIX;
	        }
	        String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);
	        return this.processResponseXml(respXml);
	    }
	    
	    public Map<String, String> sendRedPack(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception{
	    	 String url = WXPayConstants.RED_PARK;
	         String respXml = this.requestWithCert(url, this.fillRedPackData(reqData), connectTimeoutMs, readTimeoutMs);
	         return WXPayUtil.xmlToMap(respXml);
	        // return this.processResponseXml(respXml);
	    }
	    
	    public Map<String, String> sendRedPack(Map<String, String> reqData) throws Exception {
			return this.sendRedPack(reqData, config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
		}
	    
	    public Map<String, String> queryRedPack(Map<String, String> reqData, int connectTimeoutMs, int readTimeoutMs) throws Exception{
	    	 String url = WXPayConstants.QUERY_PARK;
	         String respXml = this.requestWithCert(url, this.fillRedPackData(reqData), connectTimeoutMs, readTimeoutMs);
	         return WXPayUtil.xmlToMap(respXml);
	        // return this.processResponseXml(respXml);
	    }
	    
	    public Map<String, String> queryRedPack(Map<String, String> reqData) throws Exception {
			return this.queryRedPack(reqData, config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
		}
	    
	    /**
	     * 适用于微信红包
	     * @param reqData
	     * @return
	     * @throws Exception
	     */
	    public Map<String, String> fillRedPackData(Map<String, String> reqData) throws Exception {
	      reqData.put("wxappid", config.getAppId());
	      reqData.put("mch_id", config.getMchid());
	      reqData.put("nonce_str", WXPayUtil.generateUUID());
	      reqData.put("sign", WXPayUtil.generateSignature(reqData, config.getKey(), this.signType));
	      return reqData;
	    }

		public Object transfer(HashMap<String, String> data) throws Exception {
			return this.transfer(data, config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
		}

		private Object transfer(HashMap<String, String> data, int connectTimeoutMs, int ceadTimeoutMs) throws Exception {
			 String url = WXPayConstants.TRANSFERS_URL_SUFFIX;
			 SignType signType=SignType.MD5;
			 String strSign=WXPayUtil.generateSignature(data, "950DB709DBF848ADA30C8663D45CA655", signType);
			 data.put("sign", strSign);
	         String respXml = this.requestWithCert(url, data, connectTimeoutMs, ceadTimeoutMs);
	         return WXPayUtil.xmlToMap(respXml);
		}
	    
	    
}
